from rest_framework import serializers
import re
from django_redis import get_redis_connection
from rest_framework_jwt.settings import api_settings


from user.models import User
from celery_tasks.email.tasks import send_email


class UserSerializer(serializers.ModelSerializer):
    """用户注册模块序列化器"""
    password2 = serializers.CharField(label="确认密码", write_only=True)
    sms_code = serializers.CharField(label="短信验证码", write_only=True)
    allow = serializers.CharField(label="同意协议", write_only=True)

    # 增加token字段
    token = serializers.CharField(label="JWT状态保持", read_only=True)

    # 序列化器所有字段"id", "username", "password", "password2", "mobile", "sms_code", "allow"
    # 模型的字段"id", "username", "password", "mobile",
    # 反序列化输入 username", "password", "password2", "mobile", "sms_code", "allow"
    # 序列化输出 "id", "username", "mobile",
    class Meta:
        model = User  # 指定序列化器映射的模块
        fields = ["id", "username", "password", "password2", "mobile", "sms_code", "allow", "token"]  # 指明为模型类的哪些字段生成
        # 额外参数
        extra_kwargs = {
            'username': {
                'min_length': 5,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许5-20个字符的⽤户名',
                    'max_length': '仅允许5-20个字符的⽤户名',
                }
            },
            'password': {
                'write_only': True,
                'min_length': 8,
                'max_length': 20,
                'error_messages': {
                    'min_length': '仅允许8-20个字符的密码',
                    'max_length': '仅允许8-20个字符的密码',
                }
            }
        }

    def validate_mobile(self, value):
        """校验手机"""
        if not re.match(r'^1[345789]\d{9}$', value):
            raise serializers.ValidationError("手机格式不正确")
        return value

    def validate_allow(self, value):
        """检验是否勾选协议"""
        if not value == "true":
            raise serializers.ValidationError("未勾选同意协议")

    def validate(self, attrs):
        """校验密码和验证码"""
        if attrs.get("password") != attrs.get("password2"):
            raise serializers.ValidationError("两次密码不一致")

        """校验验证码"""
        # 创建数据库实例对象
        redis_obj = get_redis_connection('verify_codes')
        # 数据库保存的验证码
        sms_code = redis_obj.get('sms_%s' % attrs.get("mobile")).decode()
        # 如果验证码不存在
        if sms_code is None:
            raise serializers.ValidationError("验证码过期")
        # 对用户和数据库的验证吗进行校验
        if attrs.get("sms_code") != sms_code:
            raise serializers.ValidationError("短信验证码错误")

        return attrs

    def create(self, validated_data):
        """创建新用户"""
        # 移除数据库不存在的字段
        del validated_data["password2"]
        del validated_data["sms_code"]
        del validated_data["allow"]
        # 创建用户对象
        user = User(**validated_data)
        # 对密码进行加密
        user.set_password(validated_data["password"])
        user.save()

        # 补充生成记录登录状态的token
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        payload = jwt_payload_handler(user)
        token = jwt_encode_handler(payload)
        user.token = token
        print(user.token)
        return user


class UserDetailSerializer(serializers.ModelSerializer):
    """用户个人信息"""

    class Meta:
        model = User  # 指定序列化器映射的模块
        fields = ["id", "username", "mobile", "email", "email_active"]  # 指明为模型类的哪些字段生成


class EmailSerializer(serializers.ModelSerializer):
    """邮箱序列化器"""

    class Meta:
        model = User
        fields = ["id", "email"]
        extra_kwargs = {
            "email": {
                "required": True,
            }
        }

    def update(self, instance, validated_data):
        """更新，instance为要更新的对象实例"""
        # 1.通过实例对象instance对email进行赋值
        instance.email = validated_data["email"]
        # 2.保存用户邮箱
        instance.save()
        # 3.生成邮箱链接(需要进行加密处理)
        # 主要为了区分是哪个用户在进行邮箱验证
        # token = generate_email_token()
        verify_url = instance.generate_email_token()
        # 4.发送邮件
        send_email.delay(instance.email, verify_url)

        return instance
